package com.budwk.app.common.component;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.extra.ssh.JschUtil;
import com.alibaba.fastjson.JSONObject;
import com.budwk.app.base.gmpass.SM4Util;
import com.budwk.app.common.config.AppException;
import com.budwk.app.sqfw.constants.CommonConstants;
import com.budwk.app.sys.models.LinuxServiceLayConfig;
import com.budwk.app.sys.services.LinuxServicelayConfigService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.starter.common.result.Result;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.Mvcs;
import org.nutz.mvc.upload.TempFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 系统打包业务公共类
 */

@IocBean
@Slf4j
public class LinuxPackageLayComponent {

    @Inject
    private SysUserService sysUserService;
    @Inject
    private LinuxServicelayConfigService linuxServicelayConfigService;

    /**
     * 校验上传基本参数
     *
     * @param tempFile
     * @param configId
     */
    public void checkUploadFileToLinuxParam(TempFile tempFile, String configId) {
        if (StringUtils.isEmpty(configId)) {
            throw new AppException("请先配置服务器!");
        }
        String suffix = tempFile.getSubmittedFileName().substring(tempFile.getSubmittedFileName().lastIndexOf(".") + 1);
        if (!"zip".equals(suffix) && !"jar".equals(suffix)) {
            throw new AppException("只能上传zip或者jar文件！");
        }
    }

    /**
     * 上传文件
     *
     * @param uploadFile
     * @param config
     */
    public boolean uploadFile(File uploadFile, LinuxServiceLayConfig config, String backFileName, String fileNewName) {
        // 1 本地服务器上传
        HttpServletRequest request = Mvcs.getReq();
        String localAddr = request.getLocalAddr();
        log.info("uploadFile localAddr info: {}, serverName: {}, getRemoteAddr: {}", localAddr, request.getServerName(), request.getRemoteAddr());
        if (judgeIsLocalServer(config)) {
            // 2 上传到本地服务器
            return uploadPackageToLocalServer(uploadFile, config, backFileName);
        }
        // 3 外部服务器上传
        return uploadPackageToExternalServer(uploadFile, config, backFileName, fileNewName);
    }

    // 上传到外部服务器
    private boolean uploadPackageToExternalServer(File uploadFile, LinuxServiceLayConfig config, String backFileName, String fileNewName) {
        log.info("externalUploadPackage start  param backFileName: {}", backFileName);
        boolean isUpload = true;
        try {
            // 上传到外部服务器
            Session session = linuxServicelayConfigService.getJSchSession(config);
            linuxServicelayConfigService.uploadFile(session, uploadFile, config.getId(), backFileName, config.getProjectPackname());
        } catch (Exception e) {
            log.error("uploadFile error:", e);
            isUpload = false;
        } finally {
            uploadFile.deleteOnExit();
        }
        return isUpload;
    }

    private boolean uploadPackageToLocalServer(File file, LinuxServiceLayConfig config, String backFileName) {
        log.info("localUploadPackage start param backFileName: {}", backFileName);
        boolean isUpload = true;
        try {
            // 2 上传到服务器
            // 文件存放路径
            String filePath = config.getFileUploadPath() + File.separator + config.getProjectPackname();
            File firstFile = new File(filePath);
            if (firstFile.exists()) {
                // 备份文件
                File backFile = new File(config.getFileUploadPath() + File.separator + backFileName);
                FileUtils.moveFile(firstFile, backFile);
            }
            // 将上传的文件复制到目录下
            FileUtils.moveFile(file, firstFile);
        } catch (Exception e) {
            log.error("uploadFile error:", e);
            isUpload = false;
        } finally {
            file.delete();
        }
        return isUpload;
    }

    /**
     * 获取需要备份的文件名称
     *
     * @param config
     * @param tempFile
     * @return
     */
    public String getBackFileName(LinuxServiceLayConfig config, TempFile tempFile) {
        String fileName = tempFile.getSubmittedFileName(); // 获取文件名
        String[] fileNames = fileName.split("\\.");
        String[] projectPacknames = config.getProjectPackname().split("\\.");
        String nowDate = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
        String backFileName = null;
        if ("jar".equals(fileNames[1])) {
            backFileName = String.format("%s%s%s%s", projectPacknames[0], "-", nowDate, ".jar");
        }
        if ("zip".equals(fileNames[1])) {
            backFileName = String.format("%s%s%s%s", projectPacknames[0], "-", nowDate, ".zip");
        }
        return backFileName;
    }

    /**
     * 判断是否为当前服务器
     *
     * @return
     */
    public boolean judgeIsLocalServer(LinuxServiceLayConfig config) {
        HttpServletRequest request = Mvcs.getReq();
        String serverName = request.getServerName();
        log.info("judgeIsLocalServer serverName: {}, getRemoteAddr: {}", serverName, request.getRemoteAddr());
        return serverName.equals(config.getIp());
    }

    /**
     * 发布到外部服务器
     *
     * @param config
     * @return
     * @throws InterruptedException
     */
    public Boolean pushToExternalServer(LinuxServiceLayConfig config) throws InterruptedException {
        log.info("pushToExternalServer entre param: {}", JSONObject.toJSONString(config));
        int index = 5;
        boolean isSuccess = false;
        for (int i = 0; i < index; i++) {
            try {
                Session session = linuxServicelayConfigService.getJSchSession(config);
                linuxServicelayConfigService.executeShell(session, config.getShellCommand());
                isSuccess = true;
                break;
            } catch (Exception e) {
                Thread.sleep(1000L);
                log.error("pushPackage error:", e);
                isSuccess = false;
            }
        }
        return isSuccess;
    }


    /**
     * 发布到当前服务器
     *
     * @param config
     * @return
     * @throws InterruptedException
     */
    public Boolean pushToLocalServer(LinuxServiceLayConfig config) throws InterruptedException {
        log.info("pushToLocalServer entre param: {}", JSONObject.toJSONString(config));
        int index = 5;
        boolean isSuccess = false;
        for (int i = 0; i < index; i++) {
            try {
                String[] cmd = new String[]{"sh", "-c", config.getShellCommand()};
                Process process = Runtime.getRuntime().exec(cmd);
                int exitValue = process.waitFor();
                if (0 != exitValue) {
                    log.debug("call shell failed. error code is :" + exitValue);
                    return true;
                }
                isSuccess = true;
                break;
            } catch (Exception e) {
                Thread.sleep(500L);
                log.error("pushPackage error:", e);
                isSuccess = false;
            }
        }
        return isSuccess;
    }


    public static void closeSession(Session session) {
        if (session != null) {
            session.disconnect();
        }
    }

    public static void closeChannelSftp(ChannelSftp sftp) {
        if (sftp != null) {
            sftp.disconnect();
        }
    }

    public void downloadHistroyLog(HttpServletResponse response, String filepath, LinuxServiceLayConfig config, String logName) {
        if (judgeIsLocalServer(config)) {
             // 当前服务器下载
            InputStream inStream = null;
            ServletOutputStream outputStream = null;
            try {
                outputStream = response.getOutputStream();
                inStream = new FileInputStream(filepath);
                // 循环取出流中的数据
                byte[] buff = new byte[1024];
                int len;
                while ((len = inStream.read(buff)) > 0) {
                    outputStream.write(buff, 0, len);
                    outputStream.flush();
                }
            } catch (IOException e) {
                log.error("downloadHistroyLog error: ", e);
                throw new AppException("文件下载失败，请联系管理员!");
            } finally {
                IOUtils.closeQuietly(outputStream);
                IOUtils.closeQuietly(inStream);
            }
            return;
        }
    }

    public Result<?> listHistroyLog(LinuxServiceLayConfig config, LocalDate queryTime) {
        if (judgeIsLocalServer(config)) {
            // 当前服务器获取
            File file = new File(String.format("%s%s%s", config.getFileUploadPath(), File.separator, "logs"));
            File[] files = file.listFiles();
            if (files != null && files.length > 0) {
                List<String> list = Arrays.stream(files).sorted(Comparator.comparing(File::lastModified).reversed()).map(File::getName).collect(Collectors.toList());
                return Result.data(list);
            }
            return Result.success();
        }
        // 外部服务器获取
        try {
            Session session = linuxServicelayConfigService.getJSchSession(config);
            ChannelSftp sftp = JschUtil.openSftp(session);
            String filepath = config.getFileUploadPath() + "/logs/";
            Vector objects = sftp.ls(filepath);
            Map<String, Date> map = new HashMap<>();
            String date = queryTime != null ? LocalDateTimeUtil.format(queryTime, DatePattern.NORM_DATE_PATTERN) : null;
            for (Object s : objects) {
                String[] s1 = String.valueOf(s).split(" ");
                String logName = s1[s1.length - 1];
                if (".".equals(logName) || "..".equals(logName)) {
                    continue;
                }
                if (StringUtils.isEmpty(date)) {
                    map.put(logName, getLastModifiedTime(filepath + logName, sftp));
                    continue;
                }
                if (logName.contains(date)) {
                    map.put(logName, getLastModifiedTime(filepath + logName, sftp));
                }
            }
            // 按照时间降序
            List<String> list = map.entrySet().stream().sorted(Map.Entry.<String, Date>comparingByValue().reversed()).map(Map.Entry::getKey)
                    .collect(Collectors.toList());
            return Result.data(list);
        } catch (Exception e) {
            log.error("listHistroyLog out error: ", e);
        }
        return Result.success();
    }

    public Date getLastModifiedTime(String srcSftpFilePath, ChannelSftp sftp)  {
        try{
            SftpATTRS sftpATTRS = sftp.lstat(srcSftpFilePath);
            return new Date(sftpATTRS.getMTime() * 1000L);
        } catch(Exception e){
            log.error("getLastModifiedTime error: {}", e);
        }
        return new Date();
    }

    /**
     * 解密密码
     * @param password
     * @return
     */
    public static String decryptPassword(String password) {
        return SM4Util.decryptEcb(CommonConstants.GENERKEY_PASSWORD, password);
    }
}
